import binascii
import machine

from umqtt.robust import MQTTClient


class yzMqttLinker():
    def __init__(self,broker_ip,user,pwd,broker_port=1883,keepalive=60):
        self._broker_ip = broker_ip
        self._broker_port = broker_port
        self._user = user
        self._pwd = pwd
        self._keepalive = keepalive
        self._ping_time = self._keepalive//2
        self._device_id = self._getDeviceId()
        self.DEBUG = False
        if self.DEBUG:
            print(self._device_id)
        self._sign = bytes(b"esp8266/")+bytes(self._device_id)+bytes(b"/")

        self._mqtt = MQTTClient(self._device_id,self._broker_ip,self._broker_port,self._user,self._pwd,self._keepalive)
        self._mqtt.set_callback(self._mqttBack)
        self._mqtt.DEBUG = False
        

        if not self._mqtt.connect(clean_session=False):
            print("New session being set up")
            self.subscribe(b"foo_topic")
            
        print("Mqtt broker connect ok")

        self._ping_timer = machine.Timer(-1)
        self._ping_timer.init(period=self._ping_time*1000, mode=machine.Timer.PERIODIC, callback=self._ping)

        self._topic_funcs = []

    def _getDeviceId(self):
        return binascii.hexlify(machine.unique_id())

    def addTopicBackFunc(self,topic,call_back_func,qos=0):
        if not topic or not call_back_func:
            print("topic or callback is none")
            return
        for _topic,func in self._topic_funcs:
            if _topic == topic:
                print("topic had exists:{}".format(topic))
                return
        
        self._topic_funcs.append((topic,call_back_func))
        self.subscribe(topic,qos)

    def _mqttBack(self,topic,payload):
        if self.DEBUG:
            print(topic)
            print(payload)
        if topic.startswith(self._sign):
            topic = topic[len(self._sign):]
        for _topic,call_func in self._topic_funcs:
            if _topic == topic:
                call_func(topic,payload)
        
    def _ping(self,t):
        self._mqtt.ping()
        if self.DEBUG:
            print("ping server...")

    def subscribe(self,topic,qos=0):
        #topic = bytes(b"esp8266/")+bytes(self._device_id)+bytes(b"/")+bytes(topic)
        topic = self._sign + bytes(topic)
        if self.DEBUG:
            print('subscribe:')
            print(topic)
        self._mqtt.subscribe(topic,qos)

    def publish(self, topic, msg, retain=False, qos=0):
        #topic = bytes(b"esp8266/")+bytes(self._device_id)+bytes(b"/")+bytes(topic)
        topic = self._sign+bytes(topic)
        if self.DEBUG:
            print('publish:')
            print(topic)
        self._mqtt.publish(topic,msg,retain,qos)

    def loop(self):
        self._mqtt.check_msg()

    def close(self):
        if self.DEBUG:
            print("close mqtt client and timer")
        self._mqtt.disconnect()
        self._ping_timer.deinit()

def test_call(topic,payload):
    print("topic call:")
    print(topic)
    print("hhhh")

def main():
    from connect_wifi import ConnectWiFi
    ConnectWiFi()
    yzm = yzMqttLinker("1.14.48.246","user","pwd")
    yzm.addTopicBackFunc(b"led1",test_call)
    try:
        while True:
            yzm.loop()
            #time.sleep(1)
    except KeyboardInterrupt as e:
        print(e)
        print("Exit.")
        yzm.close()

    
if __name__ =="__main__":
    main()
